package ga;

import java.util.LinkedHashSet;
import java.util.List;
import java.util.LinkedList;
import java.util.Set;

import model.Filter;
import model.MethodDeclared;
import model.TestClusterBuilder;
import model.ConstructorDeclared;
import operations.Operation;
import operations.InvokeMethod;
import operations.CreateObject;
import operations.AssignConstant;
import util.Randomness;
import util.GAConfig;
public class Individual implements Comparable{
	private  List<Operation> statements;
	private List<Operation> initialConstructors;
	private  float fitness = 0;
	private  float branchDistance=0;
	public Individual(List<Operation>initialConstructors,List<Operation> statements) {
		this.statements = statements;
		this.initialConstructors=initialConstructors;
	}
	public List<Operation> getInitialConstructors() {
		return initialConstructors;
	}
	public List<Operation> getStatements() {
		return statements;
	}

	public float getFitness() {
		return fitness;
	}
	
	public void setFitness(float fitness) {
		this.fitness = fitness;
	}
	public float getBranchDistance() {
		return branchDistance;
	}
	
	public void setBranchDistance(float branchDistance) {
		this.branchDistance = branchDistance;
	}
	public int getSize() {
		return this.statements.size();
	}
	
	@Override
	public String toString() {
		 StringBuilder initialCons = new StringBuilder();
		 StringBuilder peimitiveCons = new StringBuilder();
		  StringBuilder tests = new StringBuilder();
			List<Class<?>> constr=new LinkedList<Class<?>>();
			List<String> peimitiveConstr=new LinkedList<String>();
			List<Class<?>> returnConstr=new LinkedList<Class<?>>();

	      for(int i=0; i<this.statements.size();i++){
	    	  if(this.statements.get(i) instanceof InvokeMethod){
	    		  InvokeMethod m=(InvokeMethod) this.statements.get(i);
	    		     List<Class<?>> inputType =((MethodDeclared) m.getMethod()).getInputTypes();
	    		     for(Class<?> cls:inputType)
	    		    	 if(!Filter.isPrimitiveOrStringType(cls)){
	    		    		 if(!constr.contains(cls))
	    		    	 		 constr.add(cls);
	    		    	 }	    		     
	    		     	Class<?> clz=((MethodDeclared) m.getMethod()).getMethod().getReturnType();
	    		    		 if(Filter.isPrimitiveOrStringType(clz)){
		    		    		 if(!peimitiveConstr.contains(m.getReturn())){
		    		    			 peimitiveConstr.add(m.getReturn());
		    		    			 peimitiveCons.append(clz.getCanonicalName() + " "+m.getReturn()+" = null;" );
		    		    		 }
		    		    		 tests.append("  " + this.statements.get(i).toString()+"\n");
	    		    		 }
		    		    	 else{
		    		    		 if(!returnConstr.contains(clz)&&!clz.equals(void.class)){
		    		    	 		 returnConstr.add(clz);
		    		   	    	  	tests.append(clz.getCanonicalName()+ "  " + this.statements.get(i).toString()+"\n");
		    		    		 }else
			    		   	    	  	tests.append("  " + this.statements.get(i).toString()+"\n");
	    		    			 
		    		    	 }
	    		     if(!constr.contains( ((MethodDeclared) m.getMethod()).getMethod().getDeclaringClass()))
	    		    	 	constr.add( ((MethodDeclared) m.getMethod()).getMethod().getDeclaringClass());
	    	  }
	    	  else
		    		 tests.append("  " + this.statements.get(i).toString()+"\n");
	      }
	     
			for (int i = 0; i < this.initialConstructors.size(); i++){
				if(this.initialConstructors.get(i) instanceof CreateObject){
			    	CreateObject cObj=(CreateObject) this.initialConstructors.get(i);
					ConstructorDeclared cons=((ConstructorDeclared) cObj.getConstructor());					
					if(constr.contains(cons.getConstructor().getDeclaringClass()))
						initialCons.append(this.initialConstructors.get(i).toString()+"\n");
				}else
					if (this.initialConstructors.get(i) instanceof AssignConstant) {
			    		  AssignConstant cObj = (AssignConstant) this.initialConstructors.get(i);
							if (constr.contains(cObj.getType()))
								initialCons.append(this.initialConstructors.get(i).toString()+ "\n");
					}	
			}

	      return initialCons.toString()+ " "+ peimitiveCons.toString() + "\n "+ tests.toString();

	}
	@Override
	public Individual clone() {
		List<Operation> initialConstructors = new LinkedList<Operation>();	
		if(this.initialConstructors.size()!=0)
			for (int i = 0; i < this.initialConstructors.size(); i++)
				initialConstructors.add( (Operation) this.initialConstructors.get(i).clone());

		List<Operation> ops = new LinkedList<Operation>();		
		for (int i = 0; i < this.statements.size(); i++)
			ops.add( (Operation) this.statements.get(i).clone());

		return new Individual(initialConstructors, ops);
	}
	public int compareTo(Object o) {
		Individual id = (Individual) o;
		return java.lang.Float.compare(id.getFitness(), fitness);
	}

	public static void main(String[] args) {
		Randomness.reset(20);
		int size=25;
		 Set<Class<?>> allClasses = new LinkedHashSet<Class<?>>();
	      //get class from file
	      allClasses.addAll(TestClusterBuilder.readClassFromFile());

	      
	      TestClusterBuilder TestCluster = new TestClusterBuilder(allClasses);

	      List<Operation> initialConstructors= Operation.generateInitialConstructors(TestCluster);
	      List<Operation> ops=new LinkedList<Operation>();
	      ops.addAll(Operation.randomlyGenerate(TestCluster,10));

	      Individual individual=new Individual(initialConstructors,ops);
    	  System.out.println(individual.toString());
    	  individual.setBranchDistance(1);
    	  individual.setFitness(1);
      	  System.out.println("---------- clone -----------");
    	  Individual individual2=individual.clone();
          System.out.println(individual2.toString());
       	
          
          individual.setBranchDistance(1);
    	  individual.setFitness(1);
       	  individual2.setBranchDistance(2);
    	  individual2.setFitness(2);
    	  System.out.println(individual2.getFitness() + " "+individual2.getBranchDistance());
     	  System.out.println(individual.getFitness() + " "+individual.getBranchDistance());
//////////////////////////////////////////////////////////////////////////////////
		  initialConstructors= Operation.generateInitialConstructors(TestCluster);
      	  ops=new LinkedList<Operation>();
     	  ops.addAll(Operation.randomlyGenerate(TestCluster,10));
          List<Operation> test=individual.getStatements();
          test.addAll(ops);
          individual=new Individual(initialConstructors,test);
     	  System.out.println("---------------------");
          System.out.println(individual.toString());
        System.out.println(individual2.toString());

	}
}
